package com.egor.utils;

import com.egor.data.entity.Department;
import com.itextpdf.text.Font;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;

public class ExcelGeneratorForDepartment {
    private final List<Department> departments;
    private final XSSFWorkbook workbook;
    private final XSSFSheet sheet;
    private final Document document;

    public ExcelGeneratorForDepartment(List<Department> departments) {
        this.departments = departments;
        workbook = new XSSFWorkbook();
        sheet = workbook.createSheet("Departments");
        document = new Document();
    }

    public InputStream generateExcelFile() {
        writeHeader();
        write();

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            workbook.write(outputStream);
            workbook.close();
            return new ByteArrayInputStream(outputStream.toByteArray());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public ByteArrayInputStream generatePdfFile() {
        writeHeader();
        write();

        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
            PdfWriter.getInstance(document, output);
            document.open();
            document.setPageSize(PageSize.A4.rotate());
            document.newPage();
            PdfPTable pdfTable = new PdfPTable(sheet.getRow(0).getLastCellNum());
            addPdfTableData(pdfTable);
            document.add(pdfTable);

            document.close();
            workbook.close();

            return new ByteArrayInputStream(output.toByteArray());
        } catch (DocumentException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void writeHeader() {
        Row row = sheet.createRow(0);
        CellStyle style = workbook.createCellStyle();
        XSSFFont font = workbook.createFont();
        font.setBold(true);
        font.setFontHeight(16);
        style.setFont(font);

        createCell(row, 0, "ID", style);
        createCell(row, 1, "Name", style);
        createCell(row, 2, "Description", style);
        createCell(row, 3, "Date of open", style);
    }

    private void write() {
        int rowCount = 1;
        CellStyle style = workbook.createCellStyle();
        XSSFFont font = workbook.createFont();
        font.setFontHeight(14);
        style.setFont(font);
        for (Department record : departments) {
            Row row = sheet.createRow(rowCount++);
            int columnCount = 0;
            createCell(row, columnCount++, record.getId(), style);
            createCell(row, columnCount++, record.getName(), style);
            createCell(row, columnCount++, record.getDescription(), style);
            createCell(row, columnCount, record.getDateOfOpen(), style);
        }
    }

    private void createCell(Row row, int columnCount, Object valueOfCell, CellStyle style) {
        sheet.autoSizeColumn(columnCount);
        Cell cell = row.createCell(columnCount);

        if (valueOfCell instanceof LocalDate d) {
            cell.setCellValue(d.toString());
        } else if (valueOfCell instanceof Integer i) {
            cell.setCellValue(i);
        } else if (valueOfCell instanceof Long l) {
            cell.setCellValue(l);
        } else if (valueOfCell instanceof String s) {
            cell.setCellValue(s);
        }

        cell.setCellStyle(style);
    }

    private void addPdfTableData(PdfPTable table) {
        XSSFSheet worksheet = workbook.getSheetAt(0);
        for (Row row : worksheet) {
            for (int i = 0; i < row.getPhysicalNumberOfCells(); i++) {
                Cell cell = row.getCell(i);
                String cellValue = switch (cell.getCellType()) {
                    case STRING -> cell.getStringCellValue();
                    case NUMERIC -> String.valueOf(BigDecimal.valueOf(cell.getNumericCellValue()));
                    default -> "";
                };
                PdfPCell cellPdf = new PdfPCell(new Phrase(cellValue, getCellStyleForPdf(cell)));
                setBackgroundColorForPdf(cell, cellPdf);
                setCellAlignmentForPdf(cell, cellPdf);
                table.addCell(cellPdf);
            }
        }
    }

    private Font getCellStyleForPdf(Cell cell) {
        Font font = new Font();
        CellStyle cellStyle = cell.getCellStyle();
        org.apache.poi.ss.usermodel.Font cellFont = cell.getSheet()
                .getWorkbook()
                .getFontAt(cellStyle.getFontIndex());

        if (cellFont.getItalic()) {
            font.setStyle(Font.ITALIC);
        }

        if (cellFont.getStrikeout()) {
            font.setStyle(Font.STRIKETHRU);
        }

        if (cellFont.getUnderline() == 1) {
            font.setStyle(Font.UNDERLINE);
        }

        short fontSize = cellFont.getFontHeightInPoints();
        font.setSize(fontSize);

        if (cellFont.getBold()) {
            font.setStyle(Font.BOLD);
        }

        String fontName = cellFont.getFontName();
        if (FontFactory.isRegistered(fontName)) {
            font.setFamily(fontName);
        } else {
            font.setFamily("Helvetica");
        }

        return font;
    }

    private void setBackgroundColorForPdf(Cell cell, PdfPCell cellPdf) {
        short bgColorIndex = cell.getCellStyle()
                .getFillForegroundColor();
        if (bgColorIndex != IndexedColors.AUTOMATIC.getIndex()) {
            XSSFColor bgColor = (XSSFColor) cell.getCellStyle()
                    .getFillForegroundColorColor();
            if (bgColor != null) {
                byte[] rgb = bgColor.getRGB();
                if (rgb != null && rgb.length == 3) {
                    cellPdf.setBackgroundColor(new BaseColor(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF));
                }
            }
        }
    }

    private void setCellAlignmentForPdf(Cell cell, PdfPCell cellPdf) {
        CellStyle cellStyle = cell.getCellStyle();

        HorizontalAlignment horizontalAlignment = cellStyle.getAlignment();

        switch (horizontalAlignment) {
            case LEFT:
                cellPdf.setHorizontalAlignment(Element.ALIGN_LEFT);
                break;
            case CENTER:
                cellPdf.setHorizontalAlignment(Element.ALIGN_CENTER);
                break;
            case JUSTIFY:
            case FILL:
                cellPdf.setVerticalAlignment(Element.ALIGN_JUSTIFIED);
                break;
            case RIGHT:
                cellPdf.setHorizontalAlignment(Element.ALIGN_RIGHT);
                break;
        }
    }
}
